package org.sbbs.base.web.search;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
//import org.springframework.util.Assert;

import java.util.Collection;
import org.apache.commons.beanutils.ConvertUtilsBean2;
import org.sbbs.genericdao.search.Filter;
import org.sbbs.genericdao.search.Search;

public class PropertySearchBuilder {

    private static final String GROUPON_FILTER_KEY = "groupon_";// and 和 or
    // ，如果没有或者都不是则默认为and
    private static final String GROUPON_OR = "or";// and 和 or
    // ，如果没有或者都不是则默认为and
    private static final String FILTER_PREFIX = "filter_";
    private static final int DEFAULT_PAGE_SIZE = 20;
    private static final int DEFAULT_PAGE_NUMBER = 0;
    public static String param_splitor = "_";

//    public String getParam_splitor() {
//        return param_splitor;
//    }
//
//    public void setParam_splitor(String param_splitor) {
//        this.param_splitor = param_splitor;
//    }
    /*private static final String PAGE_KEY = "page";
	
     private static final String PAGESIZE_KEY = "pagesize";
     private static final String SORTNAME_KEY = "sortname";

     private static final String SORTORDER_KEY = "sortorder";*/
    public static Search BuildSearch(HttpServletRequest req, PageReqParameters prp, String splitor) {
        Search search = new Search();

        search.addFilter(builderSearchFilters(req, splitor));
        search = buildPager(req, search, prp);
        search = buildSorts(req, search, prp);
        return search;
    }

    public static Search BuildSearch(HttpServletRequest req, String splitor) {
        DefaultPageReqParameters dprp = new DefaultPageReqParameters();
        /*Search search = new Search();

         search.addFilter(builderSearchFilters(req));
         search = buildPager(req, search);
         search = buildSorts(req, search);*/
        return BuildSearch(req, dprp, splitor);
    }

    private static Search buildSorts(HttpServletRequest req, Search search, PageReqParameters prp) {
        String sortName = req.getParameter(prp.getSortNameKey());
        String sortOrder = req.getParameter(prp.getSortOrderKey());

        if (sortName != null && sortName.trim().length() > 0) {
            String[] sorts = sortName.split(",");
            for (int i = 0; i < sorts.length; i++) {
                String sort = sorts[i].trim();
                //res.setUrl(key1 + "/" + key2);
                String[] splits = sort.split(" ");
                if (splits.length == 2) {
                    boolean isAsc = (splits[1] != null && splits[1].equalsIgnoreCase("asc"));
                    if (isAsc) {
                        search.addSortAsc(splits[0]);
                    } else {
                        search.addSortDesc(splits[0]);
                    }

                } else if (splits.length == 1) {
                    boolean isAsc = (sortOrder != null && sortOrder.equalsIgnoreCase("asc"));
                    if (isAsc) {
                        search.addSortAsc(splits[0]);
                    } else {
                        search.addSortDesc(splits[0]);
                    }

                }

            }

        } else {
            search.clearSorts();
        }

        return search;
    }

    private static Search buildPager(HttpServletRequest req, Search search, PageReqParameters prp) {
        String page = req.getParameter(prp.getPageKey());
        String pageSize = req.getParameter(prp.getPageSizeKey());

        int pager, pagesize;

        try {
            pager = Integer.parseInt(page);

            if (pager > 0) {
                pager--;
            }
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            pager = DEFAULT_PAGE_NUMBER;
        }

        try {
            pagesize = Integer.parseInt(pageSize);
        } catch (NumberFormatException e) {
            pagesize = DEFAULT_PAGE_SIZE;
        }

        search.setMaxResults(pagesize);
        search.setPage(pager);
        return search;
    }

    public static Filter builderSearchFilters(HttpServletRequest req, String spliter) {
        Filter filter;
        Map<String, Object> filterParamMap = getParametersStartingWith(req, FILTER_PREFIX);
        String groupon = req.getParameter(GROUPON_FILTER_KEY);
        if (groupon != null && groupon.equalsIgnoreCase(GROUPON_OR)) {
            groupon = "or";
        } else {
            groupon = "and";
        }

        List srList = new ArrayList();
        for (Map.Entry<String, Object> entry : filterParamMap.entrySet()) {
            String filterName = entry.getKey();
            String value = (String) entry.getValue();
            // 如果value值为空,则忽略此filter.
            if (StringUtils.isNotBlank(value)) {
                Filter sr;
                if (spliter == null) {
                    sr = buildSearchFilter(filterName, value, param_splitor);
                } else {
                    sr = buildSearchFilter(filterName, value, spliter);
                }
                srList.add(sr);
            }
        }
        Filter[] sfilters = new Filter[srList.size()];
        srList.toArray(sfilters);

        if (groupon.equals(GROUPON_OR)) {
            filter = Filter.or(sfilters);
        } else {
            filter = Filter.and(sfilters);
        }

        return filter;
    }

    public static Filter builderSearchFilters(Map<String, Object> filterMap, String splitor) {
        Filter filter;
        Map<String, Object> filterParamMap = filterMap;//getParametersStartingWith(req, FILTER_PREFIX);
        String groupon = (String) filterParamMap.get(GROUPON_FILTER_KEY);
        if (groupon != null && groupon.equalsIgnoreCase(GROUPON_OR)) {
            groupon = "or";
        } else {
            groupon = "and";
        }

        List srList = new ArrayList();
        for (Map.Entry<String, Object> entry : filterParamMap.entrySet()) {
            String filterName = entry.getKey();
            if (!filterName.equalsIgnoreCase(GROUPON_FILTER_KEY)) {
                String unprefixed = filterName.substring(FILTER_PREFIX.length());
                Object value = entry.getValue();
                // 如果value值为空,则忽略此filter.
                if (value != null) {
//                    Filter sr;// = buildSearchFilter(unprefixed, value);
                    Filter sr;
                    if (splitor == null) {
                        sr = buildSearchFilter(unprefixed, value, param_splitor);
                    } else {
                        sr = buildSearchFilter(unprefixed, value, splitor);
                    }
                    srList.add(sr);
                }
            }
        }
        Filter[] sfilters = new Filter[srList.size()];
        srList.toArray(sfilters);

        if (groupon.equals(GROUPON_OR)) {
            filter = Filter.or(sfilters);
        } else {
            filter = Filter.and(sfilters);
        }

        return filter;
    }

    /**
     * @param filterName
     * @param value
     * @return
     */
    private static Filter buildSearchFilter(final String filterName, final Object value, String param_splitor) {
        String fname = filterName.replace("__", ".");
        String[] pats = fname.split(param_splitor);
        //String firstPart = StringUtils.substringBefore(filterName, "_");
        //String operatorAndDataType = StringUtils.substring(firstPart, 0, firstPart.length());
        String opType = pats[0]; //StringUtils.substring(firstPart, 0, firstPart.length() - 1);
        String daType = pats[1]; //StringUtils.substring(firstPart, firstPart.length() - 1, firstPart.length());

        SearchOperator opr;
        try {
            opr = SearchOperator.valueOf(opType.toLowerCase());

        } catch (RuntimeException e) {
            throw new IllegalArgumentException("filter名称" + filterName + ":" + opType + "没有按规则编写,无法得到属性比较类型.", e);
        }
        String propertyNameStr = pats[2];//StringUtils.substringAfter(filterName, "_");
        if (!StringUtils.isNotBlank(propertyNameStr)) {
            throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性名称.");
        }
        // Assert.isTrue(StringUtils.isNotBlank(propertyNameStr), "filter名称" + filterName + "没有按规则编写,无法得到属性名称.");

        Filter sr = new Filter();

        sr.setProperty(propertyNameStr);
        sr.setOperator(opr.getIntValue());
        if (opr.getIntValue() == 7 || opr.getIntValue() == 70 || opr.getIntValue() == 71 || opr.getIntValue() == 72) {
            sr.setOperator(7);
        } else {
            sr.setOperator(opr.getIntValue());
        }
        if (opr.getIntValue() == 7 || opr.getIntValue() == 70 || opr.getIntValue() == 71 || opr.getIntValue() == 72) {

            sr.setValue("%" + value + "%");
//        } 
//       else if (opr.getIntValue() == 71) {
//            sr.setValue(value + "%");
//        } else if (opr.getIntValue() == 72) {
//            sr.setValue("%" + value);
        } else {

            sr.setValue(convertValue2Obj(value, daType));
        }

        return sr;

    }

    /**
     * @param request
     * @param prefix
     * @return
     */
    private static Map<String, Object> getParametersStartingWith(ServletRequest request, String prefix) {

        if (request == null) {
            throw new IllegalArgumentException("Request must not be null");
        }
        if (prefix == null) {
            throw new IllegalArgumentException("filter prefix must not be null");
        }
        if (prefix.isEmpty()) {
            throw new IllegalArgumentException("filter prefix  must not be empty");
        }

        Enumeration<?> paramNames = request.getParameterNames();
        Map<String, Object> params = new TreeMap<String, Object>();
        /*
         * if ( prefix == null ) { prefix = ""; }
         */

        while (paramNames != null && paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();
            if ("".equals(prefix) || paramName.startsWith(prefix)) {
                String unprefixed = paramName.substring(prefix.length());
                String[] values = request.getParameterValues(paramName);
                if (values == null || values.length == 0) {
                    // Do nothing, no values found at all.
                } else if (values.length > 1) {
                    params.put(unprefixed, values);
                } else {
                    params.put(unprefixed, values[0]);
                }
            }
        }
        return params;
    }

    private static Object convertValue2Obj(Object value, String daType) {
        ConvertUtilsBean2 cvt = new ConvertUtilsBean2();

        Object objValue = null;
        if (daType.equalsIgnoreCase("S")) {
            objValue = cvt.convert(value, String.class);
        } else if (daType.equalsIgnoreCase("TS")) {
            objValue = cvt.convert(value, java.sql.Timestamp.class);
        }else if (daType.equalsIgnoreCase("D")) {
            objValue = cvt.convert(value, java.sql.Date.class);
        } else if (daType.equalsIgnoreCase("I")) {
            objValue = cvt.convert(value, Integer.class);
        } else if (daType.equalsIgnoreCase("F")) {
            objValue = cvt.convert(value, Float.class);
        } else if (daType.equalsIgnoreCase("B")) {
            objValue = cvt.convert(value, Boolean.class);
        } else if (daType.equalsIgnoreCase("L")) {
            objValue = cvt.convert(value, Long.class);
        } else {
            objValue = value;
        }
        return objValue;
    }

    public static String buildWhereSqlFromSearch(Search s, List<Object> paraList) {
        return generateWhereClause(s.getFilters(), s.isDisjunction(), paraList);
    }

    protected static String generateWhereClause(List<Filter> filters, boolean isDisjunction, List<Object> paraList) {
        String content = null;
        if (filters == null || filters.size() == 0) {
            return "";
        } else if (filters.size() == 1) {
            content = filterToQL(filters.get(0), paraList);
        } else {
            Filter junction = new Filter(null, filters, isDisjunction ? Filter.OP_OR : Filter.OP_AND);
            content = filterToQL(junction, paraList);
        }

        return (content == null) ? "" : " where " + content;
    }

    @SuppressWarnings("unchecked")
    protected static String filterToQL(Filter filter, List<Object> paralist) {
        String property = filter.getProperty();
        Object value = filter.getValue();
        int operator = filter.getOperator();

        // for IN and NOT IN, if value is empty list, return false, and true
        // respectively
        if (operator == Filter.OP_IN || operator == Filter.OP_NOT_IN) {
            if (value instanceof Collection && ((Collection) value).size() == 0) {
                return operator == Filter.OP_IN ? "1 = 2" : "1 = 1";
            }
            if (value instanceof Object[] && ((Object[]) value).length == 0) {
                return operator == Filter.OP_IN ? "1 = 2" : "1 = 1";
            }
        }

        switch (operator) {
            case Filter.OP_NULL:
                return property + " is null";
            case Filter.OP_NOT_NULL:
                return property + " is not null";
            case Filter.OP_IN:
                return property + " in (" + param(paralist, value) + ")";
            case Filter.OP_NOT_IN:
                return property + " not in (" + param(paralist, value) + ")";
            case Filter.OP_EQUAL:
                return property + " = " + param(paralist, value);
            case Filter.OP_NOT_EQUAL:
                return property + " != " + param(paralist, value);
            case Filter.OP_GREATER_THAN:
                return property + " > " + param(paralist, value);
            case Filter.OP_LESS_THAN:
                return property + " < " + param(paralist, value);
            case Filter.OP_GREATER_OR_EQUAL:
                return property + " >= " + param(paralist, value);
            case Filter.OP_LESS_OR_EQUAL:
                return property + " <= " + param(paralist, value);
            case Filter.OP_LIKE:
                return property + " like " + param(paralist, value);
            case Filter.OP_ILIKE:
                return "lower(" + property + ") like lower(" + param(paralist, value.toString()) + ")";
            case Filter.OP_CUSTOM:
                return property;
                    
            case Filter.OP_AND:
            case Filter.OP_OR:
                if (!(value instanceof List)) {
                    return null;
                }

                String op = filter.getOperator() == Filter.OP_AND ? " and " : " or ";

                StringBuilder sb = new StringBuilder("(");
                boolean first = true;
                for (Object o : ((List) value)) {
                    if (o instanceof Filter) {
                        String filterStr = filterToQL((Filter) o, paralist);
                        if (filterStr != null) {
                            if (first) {
                                first = false;
                            } else {
                                sb.append(op);
                            }
                            sb.append(filterStr);
                        }
                    }
                }
                if (first) {
                    return null;
                }

                sb.append(")");
                return sb.toString();
            case Filter.OP_NOT:
                if (!(value instanceof Filter)) {
                    return null;
                }
                String filterStr = filterToQL((Filter) value, paralist);
                if (filterStr == null) {
                    return null;
                }

                return "not " + filterStr;

            default:
                throw new IllegalArgumentException("Filter comparison ( " + operator + " ) is invalid.");
        }
    }

    protected static String param(List<Object> paraList, Object value) {
        if (value instanceof Class) {
            return ((Class<?>) value).getName();
        }

        if (value instanceof Collection) {
            StringBuilder sb = new StringBuilder();
            boolean first = true;
            for (Object o : (Collection) value) {
                if (first) {
                    first = false;
                } else {
                    sb.append(",");
                }
                paraList.add(o);
                sb.append(":p");
                sb.append(Integer.toString(paraList.size()));
            }
            return sb.toString();
        } else if (value instanceof Object[]) {
            StringBuilder sb = new StringBuilder();
            boolean first = true;
            for (Object o : (Object[]) value) {
                if (first) {
                    first = false;
                } else {
                    sb.append(",");
                }
                paraList.add(o);
                sb.append(":p");
                sb.append(Integer.toString(paraList.size()));
            }
            return sb.toString();
        } else {
            paraList.add(value);
            return ":p" + Integer.toString(paraList.size());
        }
    }
}
